import docker
from flask import Blueprint, jsonify, request, current_app
from app.models.system import SystemLog
from app.services.docker_image_update_service import get_docker_update_service
import time
from datetime import datetime

docker_bp = Blueprint('docker', __name__)

# 使用懒加载Docker服务
def get_docker_client():
    """获取懒加载的Docker客户端"""
    if hasattr(current_app, 'service_manager'):
        current_app.service_manager.start_service_on_demand('docker_service', auto_stop_timeout=600)
        docker_service = current_app.service_manager.get_service_instance('docker_service')
        if docker_service:
            return docker_service.get_client()
    
    # 如果懒加载失败，回退到直接连接
    try:
        client = docker.from_env()
        return client
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"连接Docker失败: {str(e)}")
        return None

@docker_bp.route('/containers', methods=['GET'])
def get_containers():
    """获取容器列表（按需启动Docker服务）"""
    try:
        # 启动Docker服务
        if hasattr(current_app, 'service_manager'):
            current_app.service_manager.start_service_on_demand('docker_service', auto_stop_timeout=600)
            docker_service = current_app.service_manager.get_service_instance('docker_service')
            if docker_service:
                # 获取查询参数
                all_containers = request.args.get('all', 'true').lower() == 'true'
                containers = docker_service.list_containers(all=all_containers)
            else:
                return jsonify({'error': 'Docker服务不可用'}), 503
        else:
            client = get_docker_client()
            if not client:
                return jsonify({'error': 'Docker服务不可用'}), 503
            all_containers = request.args.get('all', 'true').lower() == 'true'
            containers = client.containers.list(all=all_containers)
        
        # 格式化容器数据
        container_list = [
            format_container(container) for container in containers
        ]
            
        return jsonify({
            'containers': container_list,
            'count': len(container_list)
        })
    except Exception as e:
        current_app.logger.error(f"获取容器列表失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"获取容器列表失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

def _get_container_ports(container):
    """安全地获取容器端口映射"""
    try:
        port_bindings = container.attrs.get('HostConfig', {}).get('PortBindings')
        if not port_bindings:
            return []
        
        ports_list = []
        for container_port, host_ports in port_bindings.items():
            if host_ports:
                for binding in host_ports:
                    ports_list.append({
                        'container_port': container_port.split('/')[0],
                        'host_port': binding.get('HostPort', ''),
                        'protocol': container_port.split('/')[1] if '/' in container_port else 'tcp'
                    })
        return ports_list
    except Exception as e:
        current_app.logger.warning(f"无法获取容器 {container.short_id} 的端口信息: {e}")
        return []

def _get_container_health(container):
    """安全地获取容器健康状态"""
    try:
        return container.attrs.get('State', {}).get('Health', {}).get('Status', 'unknown')
    except Exception as e:
        current_app.logger.warning(f"无法获取容器 {container.short_id} 的健康状态: {e}")
        return 'unknown'

def _get_container_stats(container):
    """安全地获取容器资源使用情况"""
    if container.status != 'running':
        return None
    try:
        stats = container.stats(stream=False, one_shot=True)
        cpu_percent = 0
        cpu_delta = stats['cpu_stats']['cpu_usage']['total_usage'] - stats['precpu_stats']['cpu_usage']['total_usage']
        system_delta = stats['cpu_stats']['system_cpu_usage'] - stats['precpu_stats']['system_cpu_usage']
        
        if system_delta > 0 and cpu_delta > 0:
            online_cpus = stats.get('cpu_stats', {}).get('online_cpus', len(stats.get('cpu_stats', {}).get('cpu_usage', {}).get('percpu_usage', [1])))
            cpu_percent = (cpu_delta / system_delta) * online_cpus * 100.0

        mem_usage = stats['memory_stats'].get('usage', 0)
        mem_limit = stats['memory_stats'].get('limit', 1)
        mem_percent = (mem_usage / mem_limit) * 100 if mem_limit > 0 else 0
        
        return {
            'cpu_percent': round(cpu_percent, 2),
            'mem_usage': mem_usage,
            'mem_limit': mem_limit,
            'mem_percent': round(mem_percent, 2),
            'network': stats.get('networks', {})
        }
    except Exception as e:
        current_app.logger.warning(f"无法获取容器 {container.short_id} 的统计信息: {e}")
        return {'cpu_percent': 0, 'mem_percent': 0, 'error': '获取统计失败'}

def format_container(container):
    """格式化单个容器对象，增加健壮性"""
    try:
        return {
            'id': container.id,
            'short_id': container.short_id,
            'name': container.name,
            'status': container.status,
            'image': container.image.tags[0] if container.image.tags else container.image.id,
            'created': container.attrs.get('Created', ''),
            'state': container.attrs.get('State', {}),
            'labels': container.labels,
            'ports': _get_container_ports(container),
            'health': _get_container_health(container),
            'stats': _get_container_stats(container),
            'logs': '' # 默认为空，前端按需获取
        }
    except Exception as e:
        current_app.logger.error(f"格式化容器 {getattr(container, 'short_id', 'N/A')} 失败: {e}")
        return {
            'id': getattr(container, 'id', 'error-id'),
            'short_id': getattr(container, 'short_id', 'error'),
            'name': '格式化失败',
            'status': 'error',
            'image': 'N/A',
            'error': str(e)
        }

@docker_bp.route('/images', methods=['GET'])
def get_images():
    """获取镜像列表"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 获取镜像列表
        images = client.images.list()
        
        # 格式化镜像数据
        image_list = []
        for image in images:
            # 过滤出没有标签的中间镜像
            if not image.tags:
                continue
                
            image_data = {
                'id': image.id,
                'short_id': image.short_id,
                'tags': image.tags,
                'created': image.attrs.get('Created', ''),
                'size': image.attrs.get('Size', 0)
            }
            image_list.append(image_data)
            
        # 按创建时间排序
        image_list.sort(key=lambda x: x['created'], reverse=True)
        
        return jsonify({
            'images': image_list,
            'count': len(image_list)
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"获取镜像列表失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/containers/<container_id>/start', methods=['POST'])
def start_container(container_id):
    """启动容器"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        container = client.containers.get(container_id)
        container.start()
        
        SystemLog.log("INFO", "docker", f"启动容器: {container.name} ({container.short_id})")
        return jsonify({
            'success': True,
            'container': {
                'id': container.id,
                'name': container.name,
                'status': 'running'
            }
        })
    except docker.errors.NotFound:
        return jsonify({'error': '容器不存在'}), 404
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"启动容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/containers/<container_id>/stop', methods=['POST'])
def stop_container(container_id):
    """停止容器"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        # 获取可选的超时参数
        timeout = request.json.get('timeout', 10) if request.json else 10
        
        container = client.containers.get(container_id)
        container.stop(timeout=timeout)
        
        SystemLog.log("INFO", "docker", f"停止容器: {container.name} ({container.short_id})")
        return jsonify({
            'success': True,
            'container': {
                'id': container.id,
                'name': container.name,
                'status': 'stopped'
            }
        })
    except docker.errors.NotFound:
        return jsonify({'error': '容器不存在'}), 404
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"停止容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/pull', methods=['POST'])
def pull_image():
    """拉取镜像"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        # 获取镜像名称
        data = request.get_json()
        if not data or 'image' not in data:
            return jsonify({'error': '缺少镜像名称参数'}), 400
            
        image_name = data['image']
        
        # 拉取镜像
        client.images.pull(image_name)
        
        SystemLog.log("INFO", "docker", f"拉取镜像: {image_name}")
        return jsonify({
            'success': True,
            'image': image_name
        })
    except docker.errors.ImageNotFound:
        return jsonify({'error': '镜像不存在'}), 404
    except docker.errors.APIError as e:
        SystemLog.log("ERROR", "docker", f"拉取镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"拉取镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/containers/<container_id>/restart', methods=['POST'])
def restart_container(container_id):
    """重启容器"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        container = client.containers.get(container_id)
        container.restart()
        
        SystemLog.log("INFO", "docker", f"重启容器: {container.name} ({container.short_id})")
        return jsonify({
            'success': True,
            'container': {
                'id': container.id,
                'name': container.name,
                'status': 'running'
            }
        })
    except docker.errors.NotFound:
        return jsonify({'error': '容器不存在'}), 404
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"重启容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/containers/<container_id>/remove', methods=['DELETE'])
def remove_container(container_id):
    """删除容器"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        container = client.containers.get(container_id)
        container_name = container.name
        
        # 强制删除容器(包括运行中的)
        container.remove(force=True)
        
        SystemLog.log("INFO", "docker", f"删除容器: {container_name} ({container_id[:12]})")
        return jsonify({
            'success': True,
            'container': {
                'id': container_id,
                'name': container_name
            }
        })
    except docker.errors.NotFound:
        return jsonify({'error': '容器不存在'}), 404
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"删除容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/containers/<container_id>/logs', methods=['GET'])
def get_container_logs(container_id):
    """获取容器日志"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        # 获取查询参数
        lines = request.args.get('lines', '500')
        since = request.args.get('since', None)
        timestamps = request.args.get('timestamps', 'true').lower() == 'true'
        
        container = client.containers.get(container_id)
        
        # 获取日志
        logs = container.logs(
            stdout=True,
            stderr=True,
            timestamps=timestamps,
            tail=int(lines) if lines.isdigit() else 500,
            since=since
        )
        
        # 转换为字符串
        logs_str = logs.decode('utf-8', errors='ignore')
        
        return jsonify({
            'logs': logs_str,
            'container': {
                'id': container.id,
                'name': container.name
            }
        })
    except docker.errors.NotFound:
        return jsonify({'error': '容器不存在'}), 404
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"获取容器日志失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/<image_id>/remove', methods=['DELETE'])
def remove_image(image_id):
    """删除镜像"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        # 强制删除镜像
        client.images.remove(image_id, force=True)
        
        SystemLog.log("INFO", "docker", f"删除镜像: {image_id[:12]}")
        return jsonify({
            'success': True,
            'image': image_id
        })
    except docker.errors.ImageNotFound:
        return jsonify({'error': '镜像不存在'}), 404
    except docker.errors.APIError as e:
        SystemLog.log("ERROR", "docker", f"删除镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"删除镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

# =================== 镜像更新管理 API ===================

@docker_bp.route('/images/updates/check', methods=['POST'])
def check_image_updates():
    """检查镜像更新"""
    try:
        update_service = get_docker_update_service()
        
        # 异步检查更新
        update_service.check_updates_async()
        
        return jsonify({
            'success': True,
            'message': '开始检查镜像更新',
            'status': 'checking'
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"检查镜像更新失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/updates/status', methods=['GET'])
def get_update_status():
    """获取更新状态"""
    try:
        update_service = get_docker_update_service()
        return jsonify(update_service.update_status)
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"获取更新状态失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/updates/pull', methods=['POST'])
def pull_image_update():
    """拉取镜像更新"""
    try:
        data = request.get_json()
        if not data or 'image_info' not in data:
            return jsonify({'error': '缺少镜像信息参数'}), 400
        
        update_service = get_docker_update_service()
        image_info = data['image_info']
        
        # 异步拉取镜像
        update_service.pull_image_async(image_info)
        
        return jsonify({
            'success': True,
            'message': f'开始拉取镜像 {image_info["name"]}',
            'status': 'updating'
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"拉取镜像更新失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/updates/pull-all', methods=['POST'])
def pull_all_auto_updates():
    """拉取所有自动更新的镜像"""
    try:
        update_service = get_docker_update_service()
        
        # 检查是否有自动更新的镜像
        auto_updates = [
            img for img in update_service.update_status['available_updates'] 
            if img.get('auto_update', False)
        ]
        
        if not auto_updates:
            return jsonify({
                'success': False,
                'message': '没有设置为自动更新的镜像'
            })
        
        # 启动自动更新
        update_service.update_all_auto_images()
        
        return jsonify({
            'success': True,
            'message': f'开始更新 {len(auto_updates)} 个镜像',
            'count': len(auto_updates)
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"批量更新镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/config', methods=['GET'])
def get_images_config():
    """获取镜像配置"""
    try:
        update_service = get_docker_update_service()
        config = update_service.load_config()
        return jsonify(config)
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"获取镜像配置失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/config', methods=['POST'])
def save_images_config():
    """保存镜像配置"""
    try:
        data = request.get_json()
        if not data:
            return jsonify({'error': '缺少配置数据'}), 400
        
        update_service = get_docker_update_service()
        success = update_service.save_config(data)
        
        if success:
            return jsonify({
                'success': True,
                'message': '配置保存成功'
            })
        else:
            return jsonify({'error': '配置保存失败'}), 500
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"保存镜像配置失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/config/add', methods=['POST'])
def add_image_config():
    """添加镜像配置"""
    try:
        data = request.get_json()
        required_fields = ['name', 'registry']
        
        if not data or not all(field in data for field in required_fields):
            return jsonify({'error': '缺少必要的镜像配置参数'}), 400
        
        update_service = get_docker_update_service()
        config = update_service.load_config()
        
        # 检查镜像是否已存在
        existing_names = [img['name'] for img in config.get('images', [])]
        if data['name'] in existing_names:
            return jsonify({'error': '镜像配置已存在'}), 400
        
        # 添加新镜像配置
        new_image = {
            'name': data['name'],
            'registry': data['registry'],
            'current_version': data.get('current_version', 'latest'),
            'auto_update': data.get('auto_update', False),
            'enabled': data.get('enabled', True),
            'description': data.get('description', '')
        }
        
        if 'images' not in config:
            config['images'] = []
        config['images'].append(new_image)
        
        success = update_service.save_config(config)
        
        if success:
            return jsonify({
                'success': True,
                'message': '镜像配置添加成功',
                'image': new_image
            })
        else:
            return jsonify({'error': '保存配置失败'}), 500
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"添加镜像配置失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/config/<image_name>', methods=['DELETE'])
def remove_image_config(image_name):
    """删除镜像配置"""
    try:
        update_service = get_docker_update_service()
        config = update_service.load_config()
        
        # 查找并删除镜像配置
        images = config.get('images', [])
        original_count = len(images)
        config['images'] = [img for img in images if img['name'] != image_name]
        
        if len(config['images']) == original_count:
            return jsonify({'error': '镜像配置不存在'}), 404
        
        success = update_service.save_config(config)
        
        if success:
            return jsonify({
                'success': True,
                'message': f'镜像配置 {image_name} 删除成功'
            })
        else:
            return jsonify({'error': '保存配置失败'}), 500
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"删除镜像配置失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/local', methods=['GET'])
def get_local_images():
    """获取本地镜像列表（用于更新管理）"""
    try:
        update_service = get_docker_update_service()
        images = update_service.get_local_images()
        return jsonify({
            'success': True,
            'images': images,
            'count': len(images)
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"获取本地镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/sync-aliyun', methods=['POST'])
def sync_aliyun_images():
    """同步阿里云镜像仓库列表"""
    try:
        update_service = get_docker_update_service()
        result = update_service.sync_aliyun_images()
        
        if result['success']:
            return jsonify({
                'success': True,
                'message': result['message'],
                'images_count': result.get('images_count', 0)
            })
        else:
            return jsonify({
                'success': False,
                'message': result['message']
            }), 500
            
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"同步阿里云镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/check-aliyun-updates', methods=['POST'])
def check_aliyun_updates():
    """检查阿里云镜像更新"""
    try:
        update_service = get_docker_update_service()
        
        # 异步检查阿里云镜像更新
        import threading
        def check_updates_thread():
            update_service.check_aliyun_image_updates()
        
        thread = threading.Thread(target=check_updates_thread)
        thread.daemon = True
        thread.start()
        
        return jsonify({
            'success': True,
            'message': '开始检查阿里云镜像更新',
            'status': 'checking'
        })
    except Exception as e:
        SystemLog.log("ERROR", "docker_update", f"检查阿里云镜像更新失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/images/pull-direct', methods=['POST'])
def pull_image_direct():
    """直接拉取指定镜像"""
    try:
        data = request.get_json()
        if not data or 'image_name' not in data:
            return jsonify({'error': '缺少镜像名称参数'}), 400
        
        image_name = data['image_name']
        registry = data.get('registry', 'crpi-axr6nxua9k92sqiz.cn-hangzhou.personal.cr.aliyuncs.com')
        version = data.get('version', 'latest')
        
        # 构建完整镜像地址
        full_image_name = f"{registry}/{image_name}:{version}"
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 异步拉取镜像
        import threading
        def pull_thread():
            try:
                SystemLog.log("INFO", "docker", f"开始拉取镜像: {full_image_name}")
                image = client.images.pull(registry + "/" + image_name, tag=version)
                SystemLog.log("INFO", "docker", f"镜像拉取成功: {full_image_name}")
                
                # 更新配置文件中的镜像信息
                update_service = get_docker_update_service()
                config = update_service.load_config()
                
                # 检查是否已存在该镜像配置
                updated = False
                for img in config.get('images', []):
                    if img['name'] == image_name:
                        img['current_version'] = version
                        img['last_updated'] = datetime.now().isoformat()
                        updated = True
                        break
                
                if not updated:
                    # 如果不存在，添加新的镜像配置
                    if 'images' not in config:
                        config['images'] = []
                    config['images'].append({
                        'name': image_name,
                        'registry': registry,
                        'current_version': version,
                        'auto_update': False,
                        'enabled': True,
                        'description': f'{image_name} 镜像',
                        'last_updated': datetime.now().isoformat()
                    })
                
                config['last_updated'] = datetime.now().isoformat()
                update_service.save_config(config)
                
            except Exception as e:
                SystemLog.log("ERROR", "docker", f"拉取镜像失败: {full_image_name} - {str(e)}")
        
        thread = threading.Thread(target=pull_thread)
        thread.daemon = True
        thread.start()
        
        return jsonify({
            'success': True,
            'message': f'开始拉取镜像: {full_image_name}',
            'image': full_image_name
        })
        
    except Exception as e:
        SystemLog.log("ERROR", "docker", f"拉取镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/status', methods=['GET'])
def get_docker_config_status():
    """基于config/update.json获取Docker配置状态"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        registry_url = config_manager.get_registry_url()
        
        # 获取本地镜像状态
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
            
        local_images = {}
        try:
            images = client.images.list()
            for image in images:
                for tag in image.tags:
                    local_images[tag] = {
                        'id': image.id,
                        'size': image.attrs.get('Size', 0),
                        'created': image.attrs.get('Created', '')
                    }
        except Exception as e:
            current_app.logger.warning(f"获取本地镜像失败: {e}")
        
        # 检查每个服务的镜像状态
        service_status = []
        for service in services:
            local_image = service.get('image', '') + ':latest'
            remote_image = f"{registry_url}/{service.get('remote_image', '')}:latest"
            
            status = {
                'name': service.get('name'),
                'description': service.get('description'),
                'local_image': local_image,
                'remote_image': remote_image,
                'port': service.get('port'),
                'has_local': local_image in local_images,
                'local_info': local_images.get(local_image),
                'has_remote_tag': remote_image in local_images
            }
            service_status.append(status)
        
        return jsonify({
            'services': service_status,
            'registry_url': registry_url,
            'total_services': len(services),
            'local_images_count': len([s for s in service_status if s['has_local']]),
            'config_file': str(config_manager.config_path)
        })
        
    except Exception as e:
        current_app.logger.error(f"获取Docker配置状态失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"获取Docker配置状态失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/check-updates', methods=['POST'])
def check_config_updates():
    """基于config/update.json检查镜像更新"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        registry_url = config_manager.get_registry_url()
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        available_updates = []
        
        for service in services:
            service_name = service.get('name')
            remote_image = f"{registry_url}/{service.get('remote_image', '')}"
            
            try:
                # 检查远程镜像是否存在
                manifest = client.api.inspect_distribution(f"{remote_image}:latest")
                
                # 获取本地镜像信息
                local_image = f"{service.get('image', '')}:latest"
                local_info = None
                
                try:
                    local_img = client.images.get(local_image)
                    local_info = {
                        'id': local_img.id,
                        'created': local_img.attrs.get('Created', '')
                    }
                except:
                    pass
                
                update_info = {
                    'service': service_name,
                    'description': service.get('description', ''),
                    'local_image': local_image,
                    'remote_image': f"{remote_image}:latest",
                    'has_local': local_info is not None,
                    'needs_update': local_info is None,  # 如果本地没有则需要拉取
                    'port': service.get('port')
                }
                
                if update_info['needs_update'] or local_info is None:
                    available_updates.append(update_info)
                    
            except Exception as e:
                current_app.logger.warning(f"检查服务 {service_name} 更新失败: {e}")
        
        SystemLog.log("INFO", "docker", f"检查完成，发现 {len(available_updates)} 个可更新/缺失的镜像")
        
        return jsonify({
            'success': True,
            'available_updates': available_updates,
            'total_checked': len(services),
            'updates_available': len(available_updates),
            'check_time': int(time.time())
        })
        
    except Exception as e:
        current_app.logger.error(f"检查配置更新失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"检查配置更新失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/services', methods=['GET'])
def get_services_detailed():
    """获取详细的服务配置和状态信息"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        registry_url = config_manager.get_registry_url()
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 获取本地所有镜像
        local_images = {}
        try:
            images = client.images.list()
            for image in images:
                for tag in image.tags:
                    local_images[tag] = {
                        'id': image.id,
                        'short_id': image.short_id,
                        'size': image.attrs.get('Size', 0),
                        'created': image.attrs.get('Created', ''),
                        'size_mb': round(image.attrs.get('Size', 0) / 1024 / 1024, 1)
                    }
        except Exception as e:
            current_app.logger.warning(f"获取本地镜像失败: {e}")
        
        # 获取运行中的容器
        running_containers = {}
        try:
            containers = client.containers.list(filters={'status': 'running'})
            for container in containers:
                for tag in container.image.tags:
                    if tag not in running_containers:
                        running_containers[tag] = []
                    running_containers[tag].append({
                        'name': container.name,
                        'id': container.short_id,
                        'status': container.status,
                        'ports': _get_container_ports(container)
                    })
        except Exception as e:
            current_app.logger.warning(f"获取运行容器失败: {e}")
        
        # 构建详细的服务状态列表
        services_detailed = []
        for service in services:
            service_name = service.get('name')
            
            # 检查镜像名称是否已经包含版本标签
            local_image_base = service.get('image', '')
            remote_image_base = service.get('remote_image', '')
            
            if ':' not in local_image_base:
                version_tag = service.get('version', 'latest')
                local_image = f"{local_image_base}:{version_tag}"
            else:
                local_image = local_image_base
                
            if ':' not in remote_image_base:
                version_tag = service.get('version', 'latest')
                remote_image = f"{registry_url}/{remote_image_base}:{version_tag}"
            else:
                remote_image = f"{registry_url}/{remote_image_base}"
            
            # 本地镜像状态
            local_info = local_images.get(local_image)
            has_local = local_info is not None
            
            # 容器运行状态
            containers = running_containers.get(local_image, [])
            is_running = len(containers) > 0
            
            service_detail = {
                'name': service_name,
                'description': service.get('description', ''),
                'port': service.get('port'),
                'local_image': local_image,
                'remote_image': remote_image,
                'has_local': has_local,
                'is_running': is_running,
                'containers': containers,
                'local_info': local_info,
                'status': 'running' if is_running else ('available' if has_local else 'missing'),
                'update_available': False,  # 将在检查更新时设置
                'can_start': has_local and not is_running,
                'can_stop': is_running,
                'can_pull': True,  # 总是可以拉取
                'can_remove': has_local and not is_running
            }
            services_detailed.append(service_detail)
        
        return jsonify({
            'services': services_detailed,
            'registry_url': registry_url,
            'total_services': len(services),
            'running_services': len([s for s in services_detailed if s['is_running']]),
            'available_services': len([s for s in services_detailed if s['has_local']]),
            'missing_services': len([s for s in services_detailed if not s['has_local']]),
            'config_file': str(config_manager.config_path)
        })
        
    except Exception as e:
        current_app.logger.error(f"获取服务详细信息失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"获取服务详细信息失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/services/<service_name>/pull', methods=['POST'])
def pull_service_image(service_name):
    """拉取指定服务的镜像"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        registry_url = config_manager.get_registry_url()
        
        # 查找服务配置
        service = None
        for s in services:
            if s.get('name') == service_name:
                service = s
                break
        
        if not service:
            return jsonify({'error': f'服务 {service_name} 不存在'}), 404
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 正确构建镜像名称，避免重复版本标签
        local_image_base = service.get('image', '')
        remote_image_base = service.get('remote_image', '')
        
        if ':' not in local_image_base:
            version_tag = service.get('version', 'latest')
            local_image = f"{local_image_base}:{version_tag}"
        else:
            local_image = local_image_base
            
        if ':' not in remote_image_base:
            version_tag = service.get('version', 'latest')
            remote_image = f"{registry_url}/{remote_image_base}:{version_tag}"
        else:
            remote_image = f"{registry_url}/{remote_image_base}"
        
        current_app.logger.info(f"服务配置: {service}")
        current_app.logger.info(f"构建的镜像名: remote={remote_image}, local={local_image}")
        
        # 拉取镜像
        try:
            current_app.logger.info(f"开始拉取镜像: {remote_image}")
            
            # 使用 subprocess 直接调用 docker pull，避免 docker-py 库的问题
            import subprocess
            process = subprocess.Popen(
                ['docker', 'pull', remote_image],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            stdout, stderr = process.communicate()

            if process.returncode != 0:
                current_app.logger.error(f"拉取镜像失败: {stderr}")
                SystemLog.log("ERROR", "docker", f"拉取镜像失败: {stderr}")
                return jsonify({'error': f'拉取失败: {stderr.strip()}'}), 500
            else:
                current_app.logger.info(f"拉取镜像成功: {stdout.strip()}")
                # 拉取成功后获取镜像对象，用于后续的标签操作
                try:
                    image = client.images.get(remote_image)
                except Exception as e:
                    current_app.logger.warning(f"无法获取已拉取的镜像对象: {e}")
                    image = None
            
            # 如果本地镜像名不同，创建标签
            if image and remote_image != local_image:
                try:
                    image.tag(local_image.split(':')[0], version_tag)
                    current_app.logger.info(f"成功创建本地标签: {local_image}")
                except Exception as e:
                    current_app.logger.warning(f"创建本地标签失败: {e}")
            
            SystemLog.log("INFO", "docker", f"成功拉取服务 {service_name} 的镜像")
            
            return jsonify({
                'success': True,
                'service': service_name,
                'remote_image': remote_image,
                'local_image': local_image,
                'message': f'成功拉取 {service_name} 镜像'
            })
            
        except Exception as e:
            error_msg = f"拉取镜像异常: {str(e)}"
            current_app.logger.error(error_msg)
            SystemLog.log("ERROR", "docker", error_msg)
            return jsonify({'error': error_msg}), 500
        
    except Exception as e:
        current_app.logger.error(f"拉取服务镜像失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"拉取服务镜像失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/services/<service_name>/start', methods=['POST'])
def start_service_container(service_name):
    """启动指定服务的容器"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        
        # 查找服务配置
        service = None
        for s in services:
            if s.get('name') == service_name:
                service = s
                break
        
        if not service:
            return jsonify({'error': f'服务 {service_name} 不存在'}), 404
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 使用服务配置中的版本标签
        version_tag = service.get('version', 'latest')
        local_image = f"{service.get('image', '')}:{version_tag}"
        service_port = service.get('port')
        
        # 检查镜像是否存在
        try:
            client.images.get(local_image)
        except docker.errors.ImageNotFound:
            return jsonify({'error': f'镜像 {local_image} 不存在，请先拉取镜像'}), 400
        
        # 准备容器配置
        container_config = {
            'image': local_image,
            'name': f'mobox-{service_name}',
            'detach': True,
            'restart_policy': {'Name': 'unless-stopped'}
        }
        
        # 如果有端口配置，添加端口映射（容器内部端口80映射到主机的service_port）
        if service_port:
            container_config['ports'] = {'80/tcp': service_port}
        
        try:
            # 启动容器
            container = client.containers.run(**container_config)
            
            SystemLog.log("INFO", "docker", f"成功启动服务 {service_name} 容器")
            
            return jsonify({
                'success': True,
                'service': service_name,
                'container': {
                    'id': container.short_id,
                    'name': container.name,
                    'status': container.status
                },
                'port': service_port,
                'message': f'成功启动 {service_name} 服务'
            })
            
        except docker.errors.APIError as e:
            SystemLog.log("ERROR", "docker", f"启动容器失败: {str(e)}")
            return jsonify({'error': f'启动失败: {str(e)}'}), 500
        
    except Exception as e:
        current_app.logger.error(f"启动服务容器失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"启动服务容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/services/<service_name>/stop', methods=['POST'])
def stop_service_container(service_name):
    """停止指定服务的容器"""
    try:
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 查找服务的容器
        container_name = f'mobox-{service_name}'
        
        try:
            container = client.containers.get(container_name)
            container.stop()
            
            SystemLog.log("INFO", "docker", f"成功停止服务 {service_name} 容器")
            
            return jsonify({
                'success': True,
                'service': service_name,
                'container': {
                    'id': container.short_id,
                    'name': container.name,
                    'status': 'stopped'
                },
                'message': f'成功停止 {service_name} 服务'
            })
            
        except docker.errors.NotFound:
            return jsonify({'error': f'服务 {service_name} 的容器不存在'}), 404
        
    except Exception as e:
        current_app.logger.error(f"停止服务容器失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"停止服务容器失败: {str(e)}")
        return jsonify({'error': str(e)}), 500

@docker_bp.route('/config/check-updates-detailed', methods=['POST'])
def check_updates_detailed():
    """检查更新（增强版）- 返回详细的更新信息"""
    try:
        from app.config_manager import get_config_manager
        
        config_manager = get_config_manager()
        services = config_manager.get_services_config()
        registry_url = config_manager.get_registry_url()
        
        client = get_docker_client()
        if not client:
            return jsonify({'error': 'Docker服务不可用'}), 503
        
        # 获取本地镜像信息
        local_images = {}
        try:
            images = client.images.list()
            for image in images:
                for tag in image.tags:
                    local_images[tag] = {
                        'id': image.id,
                        'short_id': image.short_id,
                        'size': image.attrs.get('Size', 0),
                        'created': image.attrs.get('Created', ''),
                        'size_mb': round(image.attrs.get('Size', 0) / 1024 / 1024, 1)
                    }
        except Exception as e:
            current_app.logger.warning(f"获取本地镜像失败: {e}")
        
        # 检查每个服务的更新状态
        services_update_info = []
        for service in services:
            service_name = service.get('name')
            version_tag = service.get('version', 'latest')
            local_image = f"{service.get('image', '')}:{version_tag}"
            remote_image = f"{registry_url}/{service.get('remote_image', '')}:{version_tag}"
            
            local_info = local_images.get(local_image)
            has_local = local_info is not None
            
            update_info = {
                'service': service_name,
                'description': service.get('description', ''),
                'port': service.get('port'),
                'local_image': local_image,
                'remote_image': remote_image,
                'has_local': has_local,
                'local_info': local_info,
                'update_status': 'missing' if not has_local else 'checking',
                'can_pull': True,
                'can_update': True,
                'recommended_action': 'pull' if not has_local else 'check'
            }
            
            # 如果有本地镜像，尝试检查远程版本
            if has_local:
                try:
                    # 检查远程镜像存在性
                    manifest = client.api.inspect_distribution(remote_image)
                    remote_digest = manifest.get('Descriptor', {}).get('digest', '')
                    
                    # 简化的更新检查 - 如果能获取到远程信息就假设可以更新
                    update_info['update_status'] = 'available'
                    update_info['remote_info'] = {
                        'digest': remote_digest,
                        'available': True
                    }
                    update_info['recommended_action'] = 'update'
                    
                except Exception as e:
                    current_app.logger.warning(f"检查远程镜像 {remote_image} 失败: {e}")
                    update_info['update_status'] = 'check_failed'
                    update_info['error'] = str(e)
                    update_info['recommended_action'] = 'retry'
            
            services_update_info.append(update_info)
        
        # 统计信息
        total_services = len(services_update_info)
        missing_count = len([s for s in services_update_info if s['update_status'] == 'missing'])
        available_count = len([s for s in services_update_info if s['update_status'] == 'available'])
        failed_count = len([s for s in services_update_info if s['update_status'] == 'check_failed'])
        
        SystemLog.log("INFO", "docker", f"检查更新完成: 总计{total_services}, 缺失{missing_count}, 可更新{available_count}, 检查失败{failed_count}")
        
        return jsonify({
            'success': True,
            'services': services_update_info,
            'summary': {
                'total_services': total_services,
                'missing_images': missing_count,
                'available_updates': available_count,
                'check_failed': failed_count,
                'registry_url': registry_url
            },
            'check_time': int(time.time()),
            'message': f'检查完成，发现 {missing_count} 个缺失镜像，{available_count} 个可更新镜像'
        })
        
    except Exception as e:
        current_app.logger.error(f"检查详细更新失败: {str(e)}")
        SystemLog.log("ERROR", "docker", f"检查详细更新失败: {str(e)}")
        return jsonify({'error': str(e)}), 500 